package mblog.base.utils;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;

import javax.imageio.ImageIO;

import org.apache.log4j.Logger;
import org.springframework.web.multipart.MultipartFile;

import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;

public class ImageUtils
{
  private static Logger log = Logger.getLogger(ImageUtils.class);
  
  public static void validate(String dest)
    throws IOException
  {
    File destFile = new File(dest);
    if (dest == null) {
      throw new NullPointerException("Destination must not be null");
    }
    if ((destFile.getParentFile() != null) && (!destFile.getParentFile().exists()) && (!destFile.getParentFile().mkdirs())) {
      throw new IOException("Destination '" + dest + "' directory cannot be created");
    }
    if ((destFile.exists()) && (!destFile.canWrite())) {
      throw new IOException("Destination '" + dest + "' exists but is read-only");
    }
  }
  
  public static void download(String urlString, String savePath)
    throws Exception
  {
    URL url = new URL(urlString);
    URLConnection connection = url.openConnection();
    connection.setConnectTimeout(5000);
    InputStream is = connection.getInputStream();
    
    byte[] bs = new byte[1024];
    
    File sf = new File(savePath);
    if ((sf.getParentFile() != null) && (!sf.getParentFile().exists()) && 
      (!sf.getParentFile().mkdirs())) {
      throw new IOException("Destination '" + savePath + "' directory cannot be created");
    }
    OutputStream os = new FileOutputStream(savePath);
    int len;
    while ((len = is.read(bs)) != -1)
    {
      len=0;
      os.write(bs, 0, len);
    }
    os.close();
    is.close();
  }
  
  public static boolean scaleImageByWidth(MultipartFile file, String dest, int maxSize)
    throws IOException
  {
    BufferedImage src = ImageIO.read(file.getInputStream());
    int w = src.getWidth();
    int h = src.getHeight();
    
    log.debug("origin with/height " + w + "/" + h);
    
    int size = Math.max(w, h);
    int tow = w;
    int toh = h;
    if (size > maxSize) {
      if (w > maxSize)
      {
        tow = maxSize;
        toh = h * maxSize / w;
      }
      else
      {
        tow = w * maxSize / h;
        toh = maxSize;
      }
    }
    scale(file, dest, tow, toh);
    return true;
  }
  
  public static void scale(MultipartFile file, String dest, int width, int height)
    throws IOException
  {
    validate(dest);
    log.debug("scaled with/height : " + width + "/" + height);
    Thumbnails.of(new InputStream[] { file.getInputStream() }).size(width, height).toFile(dest);
  }
  
  public static void scaleByCenter(MultipartFile file, String dest, int width, int height)
    throws IOException
  {
    validate(dest);
    log.debug("scaled with/height : " + width + "/" + height);
    Thumbnails.of(new InputStream[] { file.getInputStream() }).scale(1.0D).toFile(dest);
    try
    {
      double scale = 1.0D;
      double scal2 = 1.0D;
      
      BufferedImage image = Thumbnails.of(new InputStream[] { file.getInputStream() }).scale(scale).asBufferedImage();
      if ((image.getWidth() > 400) || (image.getHeight() > 400))
      {
        scale = width / image.getWidth();
        scal2 = height / image.getHeight();
        if (image.getWidth() * scale < width) {
          scale = scal2;
        }
        if (image.getHeight() * scal2 > height) {
          scale = scal2;
        }
      }
      image = Thumbnails.of(new InputStream[] { file.getInputStream() }).scale(scale).asBufferedImage();
      
      Thumbnails.of(new BufferedImage[] { image }).scale(1.0D).sourceRegion(Positions.CENTER, width, height).outputQuality(1.0D).toFile(dest);
    }
    catch (Exception e)
    {
      Thumbnails.of(new InputStream[] { file.getInputStream() }).size(width, height).toFile(dest);
    }
  }
  
  public static void scale(File file, String dest, int width, int height)
    throws IOException
  {
    validate(dest);
    log.debug("scaled with/height : " + width + "/" + height);
    Thumbnails.of(new File[] { file }).size(width, height).toFile(dest);
  }
  
  public static boolean scaleImage(MultipartFile file, String dest, int maxSize)
    throws IOException
  {
    BufferedImage src = ImageIO.read(file.getInputStream());
    int w = src.getWidth();
    int h = src.getHeight();
    
    log.debug("origin with/height " + w + "/" + h);
    
    int size = Math.max(w, h);
    int tow = w;
    int toh = h;
    if (size > maxSize) {
      if (w > maxSize)
      {
        tow = maxSize;
        toh = h * maxSize / w;
      }
      else
      {
        tow = w * maxSize / h;
        toh = maxSize;
      }
    }
    log.debug("scaled with/height : " + tow + "/" + toh);
    
    scale(file, dest, tow, toh);
    return true;
  }
  
  public static boolean scaleImage(File file, String dest, int maxSize)
    throws IOException
  {
    BufferedImage src = ImageIO.read(file);
    int w = src.getWidth();
    int h = src.getHeight();
    
    log.debug("origin with/height " + w + "/" + h);
    
    int size = Math.max(w, h);
    int tow = w;
    int toh = h;
    if (size > maxSize) {
      if (w > maxSize)
      {
        tow = maxSize;
        toh = h * maxSize / w;
      }
      else
      {
        tow = w * maxSize / h;
        toh = maxSize;
      }
    }
    log.debug("scaled with/height : " + tow + "/" + toh);
    
    scale(file, dest, tow, toh);
    return true;
  }
  
  public static boolean cutImage(File file, String dest, int x, int y, int width, int height)
    throws IOException, InterruptedException
  {
    validate(dest);
    Thumbnails.of(new File[] { file }).sourceRegion(x, y, width, height).size(width, height).keepAspectRatio(false).toFile(dest);
    return true;
  }
  
  public static boolean cutImage(File file, String dest, int x, int y, int size)
    throws IOException, InterruptedException
  {
    return cutImage(file, dest, x, y, size, size);
  }
}
